/*
 * Created on 2006-6-27
 */
package entity;

import geometry.Circle;

import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;

/**
 * 
 * @author xiaohui
 * @email lixiaohui@comp.nus.edu.sg
 */

public class Field {
	Cell[][] grids;
	int numCellsPerRow;
	int numCellsPerCol;
	int cellLength;

	public Field(int nCells, int cellLength) {
		this.numCellsPerRow = nCells;
		this.cellLength = cellLength;
		this.numCellsPerCol = nCells;
	}

	public Field(int numCellsRow, int numCellsCol, int cellLength) {
		this.numCellsPerRow = numCellsRow;
		this.cellLength = cellLength;
		this.numCellsPerCol = numCellsCol;
		this.grids = new Cell[numCellsPerRow][numCellsPerCol];
		for (int i = 0; i < numCellsPerRow; i++) {
			for (int j = 0; j < numCellsPerCol; j++) {
				grids[i][j] = new Cell(i, j, cellLength);
			}
		}
	}

	public int getWidth() {
		return this.numCellsPerRow * this.cellLength;
	}

	public int getHeight() {
		return this.numCellsPerCol * this.cellLength;
	}

	public Cell getCell(int idx, int idy) {
		return grids[idx][idy];
	}

	/**
	 * @param currCell
	 * @return up, down, left, right neighbor cells of current cell
	 */
	public ArrayList<Cell> getNeighbors(Cell currCell) {
		ArrayList<Cell> retList = new ArrayList<Cell>();
		int row = currCell.row_num;
		int col = currCell.col_num;
		if (row > 0) {
			retList.add(this.getCell(row - 1, col));
		}
		if (col > 0) {
			retList.add(this.getCell(row, col - 1));
		}
		if (row < numCellsPerRow - 1) {
			retList.add(this.getCell(row + 1, col));
		}
		if (col < numCellsPerCol - 1) {
			retList.add(this.getCell(row, col + 1));
		}

		return retList;
	}

	/**
	 * Called during initialization for each query to have full_cell and
	 * partial_cell.
	 * 
	 * 
	 * 
	 * 
	 * // while q not empty // examine one neighbor, if intersected, add to
	 * cells, put its neighbor // into q (not examined yet) // otherwise, check
	 * next one
	 * 
	 * @param focolLoc
	 * @param cd
	 * 
	 * @return a set of cells intersected with query circle.
	 */
	// public HashSet<Cell> intersectWith(Ellipse2D queryCircle) {
	// HashSet<Cell> retCells = new HashSet<Cell>();
	// HashSet<Cell> examined = new HashSet<Cell>();
	// LinkedList<Cell> toBeChecked = new LinkedList<Cell>();
	//
	// Cell focalCell = inWhichCell(queryCircle.getCenterX(),
	// queryCircle.getCenterY());
	// // retCells.add(currCell);
	// toBeChecked.add(focalCell);
	// Cell headCell;
	// while (!toBeChecked.isEmpty()) {
	// headCell = toBeChecked.remove();
	// // need to check if not examined
	// if (!examined.contains(headCell)) {
	// examined.add(headCell);
	// // need to check if headCell intersects with the circle
	// if (headCell.isIntersectWith(queryCircle)) {
	// retCells.add(headCell);
	// ArrayList<Cell> tempArray = getNeighbors(headCell);
	// for (Cell c : tempArray) {
	// if (!examined.contains(c)) {
	// toBeChecked.add(c);
	// }
	// }
	// tempArray.clear();
	// tempArray = null;
	// }
	// }
	// }
	// return retCells;
	// }

	public ArrayList<Cell> intersectWith(Circle circle) {
		ArrayList<Cell> res = new ArrayList<Cell>();
		int xlow = (int) ((circle.getCenterP().getX() - circle.getRadius()) / this.cellLength);
		int xhigh = (int) ((circle.getCenterP().getX() + circle.getRadius()) / this.cellLength);

		int ylow = (int) ((circle.getCenterP().getY() - circle.getRadius()) / this.cellLength);
		int yhigh = (int) ((circle.getCenterP().getY() + circle.getRadius()) / this.cellLength);
		for (int i = Math.max(0, xlow); i <= Math
				.min(xhigh, numCellsPerRow - 1); i++) {
			for (int j = Math.max(0, ylow); j <= Math.min(yhigh,
					numCellsPerCol - 1); j++) {
				res.add(grids[i][j]);
			}
		}
		return res;
	}

	/**
	 * To locate a point in the grids
	 * 
	 * @param p
	 * @return the cell the point resides in
	 */
	public Cell inWhichCell(Point2D p) {
		if (p == null) {
			System.err.println("Field.inWhichCell Input point null");
		}
		int cx = (int) p.getX() / cellLength;
		int cy = (int) p.getY() / cellLength;
		if (cx >= numCellsPerRow || cy >= numCellsPerCol) {
			System.err
					.println("Field.inWhichCell array index out of bound<cx, cy>: <"
							+ cx + "," + cy + ">");
			System.exit(0);
		}
		return grids[cx][cy];
	}

	/**
	 * @param centerX
	 * @param centerY
	 * @return
	 */
	private Cell inWhichCell(double centerX, double centerY) {
		int cx = (int) centerX / cellLength;
		int cy = (int) centerY / cellLength;

		return grids[cx][cy];
	}

	public void print() {
		System.out.println("field width and height:\t" + this.numCellsPerRow
				* this.cellLength);
		// System.err.println("field height:\t" + this.height);
		// System.err.println("query range:\t" + this.r);
		System.out.println("number of cells:\t" + this.numCellsPerRow + "x"
				+ this.numCellsPerCol);
		// this.CMatrix.print();
	}


}
